#!/usr/bin/env python3

# wget.py - operations for wget
#
# Copyright (c) 2019-2023 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import glob
import json
import pathlib
from ._util import LastUpdateTime
from ._main import AppStat, AppImplBase


class AppImpl(AppImplBase):

    _ETC_CFG_PATH = "/etc/mrget/mrget.conf"

    @staticmethod
    def get_name():
        return "mrget"

    def is_installed(self):
        return len(glob.glob("/usr/lib/python*/site-packages/mrget")) > 0

    def system_get_info(self, app_data_dir):
        if not self.is_installed():
            return (AppStat.NOT_INSTALLED, None, None)

        appStat, msg = self._parseAndCheckMrgetConf()
        if appStat == AppStat.FULLY_ROBUSTIFIED:
            return (appStat, msg, LastUpdateTime.read(app_data_dir))
        elif appStat == AppStat.NOT_ROBUSTIFIED or appStat == AppStat.NOT_MANAGEABLE:
            return (appStat, msg, None)
        else:
            assert False

    def system_robustify_config(self, app_data_dir):
        self._modifyMrgetConf()
        LastUpdateTime.write(app_data_dir)

    def user_get_info(self):
        assert False

    def user_robustify_config(self, username):
        assert False

    def _parseAndCheckMrgetConf(self):
        if not os.path.exists(self._ETC_CFG_PATH):
            return (AppStat.NOT_ROBUSTIFIED, "")
        if not os.path.isfile(self._ETC_CFG_PATH):
            return (AppStat.NOT_MANAGEABLE, "%s is not a file" % (self._ETC_CFG_PATH))

        # parse error
        sectionDict = self.__readCfgFile()
        if isinstance(sectionDict, tuple):
            # parse error
            return sectionDict
        if "main" not in sectionDict:
            return (AppStat.NOT_ROBUSTIFIED, "")

        # infinite retrying
        jsonData = sectionDict["main"]
        if "retry-count" not in jsonData:
            return (AppStat.NOT_ROBUSTIFIED, "")
        if not isinstance(jsonData["retry-count"], int) or jsonData["retry-count"] < 0:
            return (AppStat.NOT_MANAGEABLE, "\"retry-count\" property in %s is invalid" % (self._ETC_CFG_PATH))
        if jsonData["retry-count"] != 0:
            return (AppStat.NOT_ROBUSTIFIED, "")

        return (AppStat.FULLY_ROBUSTIFIED, "")

    def _modifyMrgetConf(self):
        sectionDict = self.__readCfgFile()
        if not isinstance(sectionDict, dict):
            assert sectionDict[0] == AppStat.NOT_ROBUSTIFIED
            sectionDict = {}

        # infinite retrying
        sectionDict.setdefault("main", {})["retry-count"] = 0

        # save
        # FIXME: should keep empty line and comment
        os.makedirs(os.path.dirname(self._ETC_CFG_PATH), exist_ok=True)
        tmpfn = self._ETC_CFG_PATH + ".tmp"
        with open(tmpfn, "w") as f:
            f.write("[main]\n")
            f.write(json.dumps(sectionDict["main"], indent=4) + "\n")
            for sectionName, data in sectionDict.items():
                if sectionName != "main":
                    f.write("\n")
                    f.write("[%s]\n" % (sectionName))
                    f.write(json.dumps(data, indent=4) + "\n")
        os.rename(tmpfn, self._ETC_CFG_PATH)

    def __readCfgFile(self):
        sectionDict = {}
        try:
            currentSection = None
            for line in pathlib.Path(self._ETC_CFG_PATH).read_text().split("\n") + [""]:
                if currentSection is None:
                    line = line.strip()
                    if line == "" or line.startswith('#'):
                        continue
                    if line.startswith('[') and line.endswith(']'):
                        currentSection = line[1:-1]
                        currentSectionContent = ""
                        continue
                    return (AppStat.NOT_MANAGEABLE, "%s content is invalid" % (self._ETC_CFG_PATH))
                else:
                    if len(line.strip()) != 0:
                        currentSectionContent += line
                    else:
                        try:
                            sectionDict[currentSection] = json.loads(currentSectionContent)
                        except json.JSONDecodeError:
                            return (AppStat.NOT_MANAGEABLE, "%s content is invalid" % (self._ETC_CFG_PATH))
                        currentSection = None
                        del currentSectionContent
            if currentSection is not None:
                return (AppStat.NOT_MANAGEABLE, "%s content is invalid" % (self._ETC_CFG_PATH))
            return sectionDict
        except FileNotFoundError:
            return (AppStat.NOT_ROBUSTIFIED, "")
